home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 2: CDPD 1
/
Almathera Ten on Ten - Disc 2: CDPD 1.iso
/
pd
/
001-025
/
002
/
make
/
make.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-13
|
16KB
|
577 lines
#include <stdio.h>
#include <ctype.h>
#include "make.h"
/*
* MAKE - Maintain separate source files
*
* SYNOPSIS
* MK [-f file] [-a] [-n] [-d] [-i] [-k] [name] ...
* f: use 'file' instead of default makefile
* a: assume all modules are obsolete (recompile everything)
* n: don't recompile, just list steps to recompile
* d: debugging (print tree, file info)
* i: ignore return statuses from execution
* k: if errors occur, propagate error status up tree; continue.
* name: module name to recompile
*
* AUTHOR
* Landon M. Dyer, Atari Inc.
*
* INCREDIBLY HACKED OVER BY
* Eric C. Brown University of Utah.
* Fred Fish, UniSoft Systems Inc (for Commodore AMIGA)
*
* HACKS
* Added object library support (dummy names that inherit dates)
* Added source library support (real parents)
* Added direct execution capability.
* Removed script file.
* Added support for my macro based debugging package (fnf)
* Ran through "indent" to change formatting (fnf)
*/
#define INIT "~INIT" /* initialization macro */
#define DEINIT "~DEINIT" /* de-init macro */
#define BEFORE "~BEFORE" /* the per-root 'startup' method */
#define AFTER "~AFTER" /* the per-root 'wrapup' method */
char *mfiles[] = { /* default makefiles */
"makefile",
"Makefile",
#ifdef VAXVMS
"[-]makefile",
"sys$login:makefile",
#endif
#ifdef MSDOS
"..\makefile",
#endif
""
};
MACRO *mroot = (MACRO *) NULL; /* root of macro-list */
FILENODE *froot = (FILENODE *) NULL; /* root of filenode-list */
FILENODE *firstf = (FILENODE *) NULL; /* the very first filenode */
char *modnames[MAXMODS]; /* module-names mentioned in commandline */
int execstat = 0; /* nonzero if started executing */
int modcount = 0; /* #of module-names */
int debug = 0; /* nonzero: turn on debugging */
int obsolete = 0; /* nonzero: every file should be recompiled */
int noscript = 0; /* nonzero: print methods on stdout */
int ignore_errors = 0; /* nonzero: ignore error return codes */
int prop_errors = 0; /* nonzero: propagate error status up tree */
DATE bigbang; /* a date, the very earliest possible */
DATE endoftime; /* a date, the very last possible */
/*
* The following are used to save macro definitions given on the
* command line. In the unix make, it is very common to override
* a definition in the makefile via a definition on the command line.
* However, this make expands macros in each line as they are read,
* which conflicts with the need to wait until the entire file is read
* before adding command line macros to the table (to override those
* from the file). Thus the code needs to be modified to delay macro
* expansions until the last minute, before shipping the line off to
* be executed. The alternative is to implement a macro locking or
* precedence scheme. Either solution requires more work than I can
* do at the moment. Fred Fish 28-Nov-85
*/
char *cmdmacros[MAXCMDMACS]; /* Macro defs given on command line */
int cmdmcount = 0; /* Number of macro defs on command line */
static void fparse ();
static void yankdependents ();
static void addmanydepend ();
static void determ ();
static void recomp ();
main (argc, argv)
int argc;
char *argv[];
{
register int arg;
register int i;
register char *mfile = NULL;
extern DATE adate ();
extern void initrootdates ();
extern void prtree ();
DBUG_ENTER ("main");
ENABLE_ABORT;
initrootdates ();
for (arg = 1; arg < argc; ++arg) {
if (*argv[arg] == '-') {
switch (tolower (argv[arg][1])) {
case '#':
DBUG_PUSH (&argv[arg][2]);
break;
case 'f':
if (++arg >= argc) {
fputs ("-f needs filename argument.\n", stderr);
DBUG_RETURN (1);
}
mfile = argv[arg];
break;
case 'a':
obsolete = 1;
break;
case 'n':
noscript = 1;
break;
case 'd':
debug = 1;
break;
case 'i':
ignore_errors = 1;
break;
case 'k':
prop_errors = 1;
break;
default:
fputs ("Unknown switch: ", stderr);
fputc (argv[arg][1], stderr);
fputc ('\n', stderr);
break;
}
} else {
if (strchr (argv[arg], '=') != NULL) {
if (cmdmcount < MAXCMDMACS) {
cmdmacros[cmdmcount++] = argv[arg];
} else {
fputs ("Too many command line macros.\n", stderr);
DBUG_RETURN (1);
}
} else {
if (modcount < MAXMODS) {
modnames[modcount++] = argv[arg];
} else {
fputs ("Too many module names.\n", stderr);
DBUG_RETURN (1);
}
}
}
}
if (mfile != NULL) {
if (fmake (mfile) == -1) {
fputs ("Cannot open makefile '", stderr);
fputs (mfile, stderr);
fputs ("'.\n", stderr);
}
} else {
for (i = 0; *mfiles[i]; ++i) {
if (fmake (mfiles[i]) != -1) {
break;
}
}
if (!*mfiles[i]) {
fputs ("Cannot open makefile.\n", stderr);
}
}
if (debug) {
prtree ();
}
DBUG_RETURN (0);
}
/*
* Construct dependency tree from the makefile 'fn'.
* Figure out what has to be recompiled, and write a script file to do that.
*/
fmake (fn)
char *fn;
{
FILE * fp;
DBUG_ENTER ("fmake");
if ((fp = fopen (fn, "r")) == (FILE *) NULL) {
DBUG_RETURN (-1);
}
fparse (fp);
determ ();
fclose (fp);
DBUG_RETURN (0);
}
/*
* Parse the input file, defining macros and building the dependency tree.
*/
static void fparse (fp)
FILE *fp;
{
auto char ibuf[STRSIZ];
auto char ebuf[STRSIZ];
auto char *strp;
register char *tok1;
register char *tok2;
register char *s;
extern char *fgets ();
register FILENODE *lastf = (FILENODE *)NULL;
extern FILENODE *addfile ();
extern void defmac ();
extern void AddToLibrary ();
extern void escape ();
extern void addmeth ();
DBUG_ENTER ("fparse");
for (;;) {
if (fgets (ibuf, STRSIZ, fp) == NULL) {
break;
}
DBUG_3 ("inline", "got line '%s'", ibuf);
mexpand (ibuf, ebuf, STRSIZ, MACCHAR);
escape (ebuf, COMCHAR);
s = ebuf + strlen (ebuf) - 1; /* clobber last newline in string */
if (s >= ebuf && *s == '\n') {
*s = '\0';
}
DBUG_3 ("inline2", "after macro and excape processing is '%s'", ebuf);
if (ebuf[0] == '\t' || ebuf[0] == ' ') {
DBUG_2 ("meth", "looks like a method line to me");
addmeth (lastf, ebuf);
continue;
}
strp = ebuf;
if ((tok1 = token (&strp)) == NULL) {
continue;
}
if ((tok2 = token (&strp)) != NULL) {
if (STRSAME (tok2, DEFMAC)) {
DBUG_2 ("mac", "looks like a macro definition to me");
if (*strp) {
defmac (tok1, strp);
}
#if VAXVMS || MSDOS
/* Preserve old behavior. Unix and amiga behavior is */
/* to never undefine any macros, in the sense used here. */
/* This also allows macros with null expansions, which */
/* are very useful. Fred Fish */
if (!*strp) {
if (undefmac (tok1) < 0) {
fputs ("Can't undefine macro '", stderr);
fputs (tok1, stderr);
fputs ("'.\n", stderr);
}
}
#endif
continue;
} else if (STRSAME (tok2, DEPEND)) {
DBUG_2 ("mac", "looks like a dependency line to me");
addmeth (lastf, gmacro (AFTER)); /* terminate last method */
lastf = filenode (tok1); /* init lastf */
if (firstf == (FILENODE *) NULL) {
firstf = lastf;
}
lastf -> fmake = NULL;
addmeth (lastf, gmacro (BEFORE));
lastf -> fflag |= ROOTP;
addmanydepend (strp, lastf);
continue;
#ifndef FUNNYLIBS
} else if (STRSAME (tok2, ISLIB)) {
addmeth (lastf, gmacro (AFTER));
lastf = filenode (tok1);
if (firstf == (FILENODE *) NULL) {
firstf = lastf;
}
lastf -> fmake = NULL;
addmeth (lastf, gmacro (BEFORE));
lastf -> fflag |= LIBRARY;
lastf -> fflag |= ROOTP;
AddToLibrary (lastf);
/* no archives here */
/* archives and libraries are mutually exclusive */
while ((tok1 = token (&strp)) != NULL) {
(void) addfile (lastf, tok1);
}
continue;
#endif
} else {
DBUG_2 ("uh", "what kinda line is this?");
addmanydepend (strp, lastf);
}
}
}
addmeth (lastf, gmacro (AFTER));
DBUG_VOID_RETURN;
}
/*
* scan tokens from strbuf and search for libraries and archives.
* libraries look like foo [ bar baz mumble ]
* archives look like foo ( bar baz mumble )
* in either case, bar, baz, and mumble have parents of foo.
* foo is added to the parentlist, if not already on the list.
* bar, baz, and mumble are added to the dependency list of depend.
* the command *cannot* be split across newlines without causing errors.
* if you don't like that, well, life's a bitch and then you die.
*/
static void addmanydepend (strbuf, depend)
char *strbuf;
FILENODE *depend;
{
register char *tok1;
register char *tok2;
register FILENODE *parent;
register FILENODE *child;
extern FILENODE *addfile ();
extern FILENODE *addparent ();
extern void exit ();
DBUG_ENTER ("addmanydepend");
DBUG_4 ("dep", "add dependencies '%s' to '%s'", strbuf, depend -> fname);
tok1 = token (&strbuf);
if (tok1 == NULL) {
DBUG_VOID_RETURN;
}
tok2 = token (&strbuf);
while (tok2 != NULL) {
#ifdef FUNNYLIBS
if (STRSAME (tok2, BGNLIB)) {
parent = addparent (tok1); /* add tok1 to parent list */
for (tok1 = token (&strbuf); /* skip over token in tok2 */
tok1 != NULL && strcmp (tok1, ENDLIB); /* go eol or end */
tok1 = token (&strbuf)) { /* get next token */
if (tok1 == NULL) {
fputs ("MAKE: Error in library defn.\n", stderr);
exit (2);
}
child = addfile (depend, tok1);
child -> fflag = LIBRARY;
child -> parent = parent;
} /* for */
tok1 = token (&strbuf);
tok2 = token (&strbuf);
continue; /* the while */
} /* if islib */
#endif
if (STRSAME (tok2, BGNARC)) {
parent = addparent (tok1); /* add tok1 to parent list */
for (tok1 = token (&strbuf); /* skip over token in tok2 */
tok1 != NULL && strcmp (tok1, ENDARC); /* go eol or end */
tok1 = token (&strbuf)) { /* get next token */
if (tok1 == NULL) {
fputs ("MAKE: Error in archive defn.\n", stderr);
exit (2);
}
child = addfile (depend, tok1);
child -> fflag = ARCHIVE;
child -> parent = parent;
} /* for */
tok1 = token (&strbuf);/* get current token */
tok2 = token (&strbuf);/* get lookahead token */
continue; /* the while */
} /* if isarc */
else { /* nothing special -- */
(void) addfile (depend, tok1);/* add dependency */
tok1 = tok2; /* shift token */
tok2 = token (&strbuf);
}
} /* while */
if (tok2 == NULL && tok1 != NULL) { /* last token = not special */
(void) addfile (depend, tok1);
}
DBUG_VOID_RETURN;
}
/*
* Determine sequence of recompiles from the creation dates.
* If have anything to recompile, then create a script file full of commands.
*/
static void determ ()
{
register FILENODE *f;
register int i;
register char *m;
extern void cleanuparchives ();
DBUG_ENTER ("determ");
if (firstf == (FILENODE *) NULL) { /* empty tree */
puts ("No changes.");
DBUG_VOID_RETURN;
}
if (modcount == 0) {
examine (firstf, endoftime);
} else {
for (i = 0; i < modcount; ++i) {
if ((f = gfile (modnames[i])) == (FILENODE *) NULL) {
fputs ("Don't know how to make ", stderr);
fputs (modnames[i], stderr);
fputs (".\n", stderr);
continue;
}
if ((f -> fflag & ROOTP) == 0) {
fputc ('\'', stderr);
fputs (f -> fname, stderr);
fputs ("' is not a root!\n", stderr);
continue;
}
examine (f, endoftime);
}
}
if (execstat) {
if ((m = gmacro (DEINIT)) != NULL) {
execute (m, noscript);
}
cleanuparchives ();
} else {
puts ("No changes.");
}
DBUG_VOID_RETURN;
}
/*
* Examine filenode 'fnd' and see if it has to be recompiled.
* 'date' is the last-touched date of the node's father
* (or 'endoftime' if its a root file.)
* Root files with NO dependencies are assumed not to be up to date.
*/
examine (fnd, date)
FILENODE *fnd;
DATE date;
{
register int rebuildp = 0;
register int rval;
register int errcode = 0;
register NODE *n;
extern void getdate ();
extern char *printdate ();
DBUG_ENTER ("examine");
DBUG_3 ("ex", "parent node date '%s'", printdate (date));
DBUG_3 ("ex", "examine node '%s'", fnd -> fname);
getdate (fnd);
DBUG_3 ("ex", "modification date '%s'", printdate (fnd -> fdate));
DBUG_3 ("ex", "parent node date '%s'", printdate (date));
if (fnd -> fnode == (NODE *) NULL && fnd -> fflag & ROOTP) {
DBUG_2 ("root", "node, is rootnode with no dependents, rebuild");
rebuildp = 1;
} else { /* see if dependents need to be recompiled */
for (n = fnd -> fnode; n != (NODE *) NULL; n = n -> nnext) {
if ((rval = examine (n -> nfile, fnd -> fdate)) != 0) {
if (rval == ERROR) {
errcode = ERROR;/* if error occurred, propagate up */
fnd -> fflag |= ERROR;
fputs ("Couldn't remake ", stderr);
fputs (fnd -> fname, stderr);
fputs (" because of errors.\n", stderr);
}
rebuildp = 1;
}
}
}
DBUG_3 ("ex", "parent node date '%s'", printdate (date));
DBUG_3 ("rebuildp", "rebuild flag is %d", rebuildp);
/* if ancestor recompiled or root, recompile, */
/* but not if error in ancestor */
if (rebuildp && (fnd -> fflag & ERROR) == 0) {
DBUG_3 ("rebuild", "'%s' needs remaking", fnd -> fname);
recomp (fnd);
if (fnd -> fflag & ERROR) {
DBUG_3 ("err", "got an error remaking %s", fnd -> fname);
DBUG_RETURN (ERROR);
}
}
DBUG_3 ("ex", "current node date now '%s'", printdate (fnd -> fdate));
DBUG_3 ("ex", "parent node date '%s'", printdate (date));
if (obsolete || laterdt (fnd -> fdate, date) >= 0) {
DBUG_2 ("date", "looks like parent needs remaking now");
rebuildp = 1;
}
if (errcode) {
DBUG_RETURN (errcode);
} else {
DBUG_RETURN (rebuildp);
}
}
/*
* Make sure a filenode gets recompiled.
*/
static void recomp (f)
FILENODE *f;
{
register char *m;
DBUG_ENTER ("recomp");
if (!execstat) {
execstat = 1;
if ((m = gmacro (INIT)) != NULL) {
execute (m, noscript);
}
}
if (f -> fflag & REBUILT) {
DBUG_VOID_RETURN;
}
if (!noscript) { /* don't extract if printing steps */
yankdependents (f);
}
if (f -> fmake != NULL) {
if (execute (f -> fmake, noscript) != 0) {
if (!ignore_errors && !prop_errors) {
exit (2);
} else if (prop_errors) {
f -> fflag |= ERROR;
}
}
}
f -> fflag |= REBUILT;
DBUG_VOID_RETURN;
}
static void yankdependents (fnd)
FILENODE *fnd;
{
register NODE *n;
extern int extract ();
DBUG_ENTER ("yankdependents");
for (n = fnd -> fnode; n != (NODE *) NULL; n = n -> nnext) {
#ifdef YANKDESCENDANTS
yankdependents (n -> nfile);
#endif
DBUG_3 ("dep", "yanking %s", n -> nfile -> fname);
DBUG_3 ("dep", "flags %d", n -> nfile -> fflag);
if ((n -> nfile -> fflag & ARCHIVE) && ((n -> nfile -> fflag & EXTRACT) == 0)) {
/* if archived and not extracted */
fputs ("Extracting ", stdout);
puts (n -> nfile -> fname);
if (!noscript) {
#ifdef LAR
if (extract (n -> nfile) == FAILURE) {
fputs ("Extract failed -- I think I'll die now.\n", stderr);
exit (1);
}
#else
fputs ("No support for archives, bye!\n", stderr);
exit (1);
#endif
}
n -> nfile -> fflag |= EXTRACT;
}
}
DBUG_VOID_RETURN;
}
/*
* Complain about being out of memory, and then die.
*/
allerr ()
{
fputs ("Can't alloc -- no space left (I give up!)\n", stderr);
exit (1);
}